package com.example.coroutines

import kotlinx.coroutines.*
import org.junit.Test
import java.io.BufferedReader
import java.io.FileReader


/**
 * 取消协程作用域
 * 1) 取消作用域会取消他的子协程
 * 2）被取消的子协程并不影响其他的兄弟
 * 3）协程通过抛出一个异常CancellationException来处理取消操作
 * 4）所有kotlinx.coroutines中的挂起函数都是可以取消的
 *
 * @author zhouronghua
 * @time 2021/12/29 8:42 上午
 */
class ScopeCancelTest {

    /**
     * 测试取消协程作用域
     * 说明：
     * 1) 取消作用域，作用域内的协程会全部取消
     * 2) 取消作用域，作用域内的协程不能捕捉到取消异常
     *
     * @author zhouronghua
     * @time 2021/12/29 8:43 上午
     */
    @Test
    fun testScopeCancel() = runBlocking {
        val scope = CoroutineScope(Dispatchers.Default)
        scope.launch {
            try {
                delay(1000)
                println("job 1 finished")
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        scope.launch {
            try {
                delay(1000)
                println("job 2 finished")
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        delay(500)
        scope.cancel()
        println("scope canceled")
    }

    /**
     * 取消作用域内的协程任务
     * 说明：取消协程作用域内的一个Job，这个Job能够捕捉到取消异常
     *
     * @author zhouronghua
     * @time 2021/12/30 8:46 上午
     */
    @Test
    fun testScopeCancelJob() = runBlocking {
        val scope = CoroutineScope(Dispatchers.Default)
        val job1 = scope.launch {
            try {
                delay(1000)
                println("job 1 finished")
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        val job2 = scope.launch {
            try {
                delay(1000)
                println("job 2 finished")
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        delay(500)
        println("scope job1 canceled")
        job1.cancel()
        delay(1500)
    }

    /**
     *  测试父协程join子协程
     *  说明：子协程join到父协程以后，挂起点取消后的任务仍然执行
     *
     * @author zhouronghua
     * @time 2021/12/30 8:46 上午
     */
    @Test
    fun testJobJoin() = runBlocking {
        println("=== 父协程 === ${Thread.currentThread().name}")
        val job1 = GlobalScope.launch {
            println("=== 子协程 === ${Thread.currentThread().name}")
            try {
                delay(1000)
            } catch (e: Exception) {
                e.printStackTrace()
            }
            // 取消后不会执行。除非子协程关联到主协程
            println("job1 finished")
            println("=== 子协程 === ${Thread.currentThread().name}")
        }
        delay(100)
        println("取消job1协程")
        job1.cancel(CancellationException("已经取消"))
        job1.join()
    }

    /**
     * 取消CPU密集型任务
     * 说明：CPU密集型任务会一直执行，不会中断，防止中断过程丢失中间结果
     * @author zhouronghua
     * @time 2021/12/30 21:19 下午
     */
    @Test
    fun `测试取消CPU密集型任务`() = runBlocking {
        val startTime = System.currentTimeMillis();
        // 启动一个子协程
        val job = launch {
            var nextTime = startTime
            var i = 0
            while (i < 15) {
                if (System.currentTimeMillis() >= nextTime) {
                    println("job I'm sleeping ${i++} ...")
                    nextTime += 500
                }
            }
        }
        println("main: 我开始等待，挂起了...")
        // 主协程进入挂起，子协程可以执行.子协程是CPU密集型任务，
        // 只有子协程执行完成才能主协程恢复
        delay(1300)
        println("main: I'm tried of waiting")
        job.cancelAndJoin()
        println("main: 我已经结束")
    }

    /**
     * yield取消CPU密集型任务
     * 说明：yield出让执行权给其他协程
     * @author zhouronghua
     * @time 2021/12/30 21:19 下午
     */
    @Test
    fun `yield取消CPU密集型任务`() = runBlocking {
        val startTime = System.currentTimeMillis();
        // 启动一个子协程
        val job = launch {
            var nextTime = startTime
            var i = 0
            while (i < 15) {
                if (System.currentTimeMillis() >= nextTime) {
                    // 子协程出让调度权
                    yield()
                    println("job I'm sleeping ${i++} ...")
                    nextTime += 500
                }
            }
        }
        println("main: 我开始等待，挂起了...")
        // 主协程进入挂起，子协程可以执行.子协程是CPU密集型任务，
        // 只有子协程执行完成才能主协程恢复
        delay(1300)
        println("main: I'm tried of waiting")
        job.cancelAndJoin()
        println("main: 我已经结束")
    }

    /**
     * 协程取消副作用
     * 说明: 协程处理需要释放资源
     *
     * @author zhouronghua
     * @time 2021/12/30 21:19 下午
     */
    @Test
    fun `协程取消副作用`() = runBlocking {
        val job = launch {
            try {
                repeat(1000) { i ->
                    println("job $i: I 'm sleeping....")
                    delay(500)
                }
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                // 释放资源
                println("job: 资源回收....")
            }
        }
        println("主协程开始等待")
        delay(1300)
        job.cancelAndJoin()
        println("主协程结束")
    }

    /**
     * 测试文件读取
     * 说明: 测试使用use读取文件
     *
     * @author zhouronghua
     * @time 2021/12/30 21:19 下午
     */
    @Test
    fun test_use_function() = runBlocking {
        println("读取文件 ${Thread.currentThread().name}")
        var br = BufferedReader(FileReader("/Users/qwe/资料/long_text_2021-05-17-12-05-56.txt"))
        with(br) {
            var line: String?
            while (true) {
                line = br.readLine() ?: break
                println("$line")
            }
            br.close()
        }
        withContext(Dispatchers.IO) {
            // 再次读取文件
            println("再次读取文件 ${Thread.currentThread().name}")
            br = BufferedReader(FileReader("/Users/qwe/资料/long_text_2021-05-17-12-05-56.txt"))
            br.use {
                var line: String?
                while (true) {
                    line = br.readLine() ?: break
                    println("$line")
                }
            }
        }
    }
}